﻿/* 
* File:   Repository.h
* Author: Wardell Bagby
*      
*/
#include "Entry.h"
namespace Project3 
{ 
	using namespace System; 
	using namespace System::IO; 
	using namespace System::Collections; 
	using namespace System::Collections::Generic;
	using namespace System::Text; 
	using namespace Project3;
	using namespace System::Windows::Forms;
	/**The PreRepository class isn't a good name for it. It holds all of the entries, does duplication checking for the entries,
	and makes sure that they have the proper ham and spam counts.**/
	public ref class PreRepository 
	{ 
	public: 
		bool sorted;
		int spamCount;
		int hamCount;
		List<Entry^>^ entries;
		PreRepository() 
		{ 
			sorted = false;
			entries = gcnew List<Entry^>();
			spamCount = hamCount =0;
		} 
		
		Entry^ currEntry; 
		/**Sets the total Spam and total Ham counts for all of the currently held entries.**/
		void setCounts(int s, int h)
		{
			spamCount =s;
			hamCount =h;
			Generic::IEnumerator<Entry^>^ e = entries->GetEnumerator();
			while(e->MoveNext())
			{
				safe_cast<Entry^>(e->Current)->setTotalNumberOfHam(h);
				safe_cast<Entry^>(e->Current)->setTotalNumberOfSpam(s);
				safe_cast<Entry^>(e->Current)->calculateHamPercentage();
				safe_cast<Entry^>(e->Current)->calculateSpamPercentage();
			}
		};
		/**Adds one to the total Ham count for every entry.**/
		void addHamCount()
		{
			hamCount++;
			setCounts(spamCount,hamCount);
		};
		/**Adds one to the total Ham count for every entry.**/
		void addSpamCount()
		{
			spamCount++;
			setCounts(spamCount,hamCount);
		};
		/**Adds a new Entry to the list, or increments the Entry's Ham/Spam count if it's already in the list.**/
		void addEntry(String^ s, bool isSpam) 
		{ 
			System::Collections::IEnumerator^ e = entries->GetEnumerator(); 
			bool found = false; 
			while(e->MoveNext())
			{ 
				currEntry = safe_cast<Entry^>(e->Current);
				//Checks to see if entry is already in List. If it is, increments it's ham/spam count. Otherwise, adds a new Entry with 1 to it's ham/spam count 
				if((currEntry->GetWord()->Equals(s))) 
				{            
					found = true; 
					if(isSpam) 
					{
						currEntry->addSpamOccurrence(); 
						currEntry->calculateSpamPercentage();
					}
					else
					{
						currEntry->addHamOccurrence(); 
						currEntry->calculateHamPercentage();
					}
				} 
			} 
			if(!found) 
			{ 
				sorted = false;
				if(isSpam) 
				{
					Entry^ e = gcnew Entry(s,1,0,0,0,0,0,0);
					e->setTotalNumberOfHam(spamCount);
					e->setTotalNumberOfSpam(hamCount);
					e->calculateHamPercentage();
					e->calculateSpamPercentage();
					entries->Add(e);
				}
				else
				{
					Entry^ e = gcnew Entry(s,0,1,0,0,0,0,0);
					e->setTotalNumberOfHam(spamCount);
					e->setTotalNumberOfSpam(hamCount);
					e->calculateHamPercentage();
					e->calculateSpamPercentage();
					entries->Add(e);
				}
			} 
		} 
		/**Returns a list of sorted entries.**/
		List<Entry^>^ getEntries() 
		{ 
			if(sorted)
				return entries; 
			entries->Sort();
			sorted = true;
			return entries;
		}; 
		/**Returns a new line delimited String with the entry's vital information delimited by a colon. Perfect for writing to a file.**/
		String^ getEntriesAsString() 
		{ 
			if(!sorted)
			{
				entries->Sort();
			}
			StringBuilder^ sB = gcnew StringBuilder(); 
			Entry^ currEntry; 
			for(int i=0; i<entries->Count; i++) 
			{ 
				currEntry = (Entry^)entries[i]; 
				sB->AppendLine(currEntry->GetWord()+":"+currEntry->GetCountInHam()+":"+currEntry->GetCountInSpam()+":"+currEntry->getTotalNumberOfHam()+":"+currEntry->getTotalNumberOfSpam()+":"+currEntry->GetPercentageInHam()+":"+currEntry->GetPercentageInSpam()+":"+currEntry->getSpamIndication()); 
			} 
			return sB->ToString(); 
		}; 
	}; 
	/**Repository handles reading in from a file to create Entries and writing out Entries to a file**/
	public ref class Repository 
	{ 
	private: 
		FileStream^ outputFile; 
		FileStream^ currentEmail; 
		List<String^>^ currentEmailWords; //For duplication checking in one email, I store the current words of an email in here, then check against it when adding a new entry.
		bool isSpam; 
		PreRepository^ preR;         

	public: 
		/**Default constructor.**/
		Repository()
		{
			outputFile = nullptr;
			preR = gcnew PreRepository();
			currentEmailWords = gcnew List<String^>();
			currentEmailWords->Add(""); //To check against empty String. 
			
			currentEmailWords->Add(nullptr); //To check against null points. (Which somehow becomes an issue when reading in from a file when using delimiters? It's strange.)
		}
		/**Sets the output file in the Constructor.**/
		Repository(FileStream^ oFile) 
		{ 
			Repository();
			setOutputFile(oFile); 
		}
		/**Sets the current email and the current spam type.**/
		bool setCurrentEmail(FileStream^ email,bool spam) 
		{			
			currentEmail = email;
			currentEmailWords->Clear();
			currentEmailWords->Add("");
			currentEmailWords->Add(nullptr);
			isSpam = spam;
			return true; 

		}; 
		/**Sets the current email, assumming that the first line tells whether the email is Spam or not.**/
		bool setCurrentEmail(FileStream^ email) 
		{ 
			isSpam = true;
			currentEmail = email;
			currentEmailWords->Clear();
			currentEmailWords->Add("");
			currentEmailWords->Add(nullptr);
			StreamReader^ sR = gcnew StreamReader(currentEmail); 
			String^ line = sR->ReadLine()->ToLower();
			try
			{
				isSpam = Convert::ToBoolean(line);
			}
			catch(Exception^ e)
			{
				if(line->Equals("spam"))
				{
					isSpam = true;
				}
				else if(line->Equals("ham"))
				{
					isSpam=false;
				}
			}
			if(isSpam)
				preR->addSpamCount();
			else
				preR->addHamCount();
			return isSpam; 

		}; 
		/**Should be called after setCurrentEmail. Actually creates the Entry objects that PreRepository holds. PreRepository handles all of 
		the duplication checking.**/
		void createEntries() 
		{ 
			StreamReader^ sR = gcnew StreamReader(currentEmail);     
			String^ line; 
			String^ delim = "[ :!.?-_=+()*&%$@\",/\n]"; 
			while((line = sR->ReadLine())!=nullptr) 
			{ 
				array<String^>^ words = line->Split(delim->ToCharArray()); 
				for(int i=0; i<words->Length; i++) 
				{ 
					if(!currentEmailWords->Contains(words[i]->ToLower()))
					{
						preR->addEntry(words[i]->ToLower(),isSpam); 
						currentEmailWords->Add(words[i]->ToLower());
					}
				} 
			} 
		} 
		/**Sets the output file. Forces it to be opened writable.**/
		bool setOutputFile(FileStream^ oFile) 
		{
			oFile->Close();
			outputFile = File::OpenWrite(oFile->Name);
			return true; 
		} 
		/**Uses a convenience method in PreRepository to get the Entries as a new line and colon delimited String. This will write to the 
		output file. WARNING: WILL NOT CHECK FOR DUPLICATES IN THE OUTPUT FILE THAT MIGHT HAVE ALREADY BEEN THERE. **/
		void buildOutputFile() 
		{ 
			String^ entries = preR->getEntriesAsString(); 
			outputFile->Close();
			outputFile = File::OpenWrite(outputFile->Name);
			StreamWriter^ out = gcnew StreamWriter(outputFile); 
			out->Write(entries); 
			out->Flush(); 
			out->Close(); 
			outputFile->Close();
			MessageBox::Show(nullptr,outputFile->Name+" has been saved successfully.","Success!",MessageBoxButtons::OK,MessageBoxIcon::Information);
		};
	}; 
}